implement igtut05;

include "sys.m";
    sys: Sys;

include "draw.m";
    draw: Draw;
    Point, Rect, Pointer, Image, Display, Font, Screen: import draw;

include "tk.m";
include "wmclient.m";
    wmclient: Wmclient;
    Window: import wmclient;
        
igtut05: module
{
    init: fn(ctxt: ref Draw->Context, args: list of string);
};

KEYBD_UP:    con 57362;
KEYBD_DOWN:  con 57363;
KEYBD_LEFT:  con 57364;
KEYBD_RIGHT: con 57365;

window: ref Window;
backbuffer: ref Image;
backbufrect: Rect;
bgcolor: ref Image;
ticks: chan of int;

starttime: int;
currtime: int;
framerate: con 30;

# Lets define some colors later use
red: ref Image;
blue: ref Image;
white: ref Image;
yellow: ref Image; 

# Define some fonts
textfont: ref Font;

# Lets define some global variables to hold the various input device states
MouseState: adt
{
    x: int;
    y: int;
    buttons: int;
};

KeyState: adt
{
    last: int;
    value: int;
};

# Initlialize the device state variables
mouse: MouseState = (0, 0, 0);
keybd: KeyState = (0, 0);

init(ctxt: ref Draw->Context, args: list of string)
{
    sys = load Sys Sys->PATH;
    draw = load Draw Draw->PATH;
    wmclient = load Wmclient Wmclient->PATH;
    
    ticks = chan of int;
    
    # Lets run this program in a new process group so we don't muck with the
    # program that started it.
    sys->pctl(Sys->NEWPGRP, nil);
    
    wmclient->init();
    
    if (ctxt == nil)
        ctxt = wmclient->makedrawcontext();
        
    buts := wmclient->Resize | 
            wmclient->Hide | 
            wmclient->Help | 
            wmclient->OK;
            
    window = wmclient->window(ctxt, "Inferno Graphics Tutorial #5", buts);
    
    winsz: Rect;
    winsz.min.x = 0;
    winsz.min.y = 0;
    winsz.max.x = 640;
    winsz.max.y = 480;
    
    window.reshape(winsz);
    window.startinput("kbd" :: "ptr" :: nil);
    
    window.onscreen("place");
    
    # Allocate an Image for background color.  This will be reused quite a bit
    # so it it defined globally
    red = window.display.color(Draw->Red);
    blue = window.display.color(Draw->Blue);
    white = window.display.color(Draw->White);
    yellow = window.display.color(Draw->Yellow);
    bgcolor = window.display.color(Draw->Black);
    
    # Load the Fonts
    textfont = loadfont( window.display, "*default*" );
    
    drawwindow(window);
    
    # Launch the time thread.  This timer thread is locked to 30 FPS.  
    spawn timer(ticks);
    
    for(;;) {
        alt {
            # Process Window Management Events
            ctl := <-window.ctl or ctl = <-window.ctxt.ctl =>
                window.wmctl(ctl);
                if(ctl != nil && ctl[0] == '!')
                    drawwindow( window );
                if(ctl != nil && ctl == "help")
                    sys->print("Help Pressed.\n");
                if(ctl != nil && ctl == "ok")
                    sys->print("OK Pressed.\n");
                if(ctl != nil && ctl == "exit")
                    sys->print("Closing app.\n");
                    break;
            # Process Keyboard Events
            kbd := <-window.ctxt.kbd =>
                prockeybd(kbd);
            # Process Mouse Events
            p := <-window.ctxt.ptr =>
                window.pointer(*p);
                procptr( window, p );
            # Lock the framerate 
            <-ticks =>   
                update();
                drawwindow( window );
        }
    }
}

update()
{
    # Here is where we will do updates to things like 
    # Animations, Sprites, Explosions, etc.  
    # Doing nothing right now, but it makes sense for you to get used to it being
    # here.
}

timer(ch: chan of int)
{
    ch <-= sys->pctl(0, nil);
    
    for(;;)
    {
        inittimer();
        if (getticks() < 1000 / framerate)
        {
            sys->sleep( (1000 / framerate) - getticks() );
        }
        ch <-= 1;
    }
}

inittimer()
{
    starttime = sys->millisec();
    currtime = starttime;
}

getticks(): int
{
    currtime = sys->millisec();
    return currtime - starttime;
}

prockeybd(keycode: int)
{
    keybd.last = keybd.value;
    keybd.value = keycode;
}

procptr(win: ref Window, pt: ref Pointer)
{
    if (win.image.r.contains( pt.xy ))
    {
        mouse.buttons = pt.buttons;

        # Now we are going to convert the global mouse coordinates
        # to local image Rect coordinates
        mouse.x = pt.xy.x - window.image.r.min.x;
        mouse.y = pt.xy.y - window.image.r.min.y;
    }
}

loadfont(display: ref Display, name: string): ref Font
{
    return Font.open(display, name);
}

drawtext(image: ref Image, text: string, font: ref Font, color: ref Image, x: int, y :int)
{
    # First lets calculate the local coordinates within the image
    dx := image.r.min.x + x;
    dy := image.r.min.y + y;
        
    p: Point = (dx, dy);
    
    image.text(p, color, (0,0), font, text);
}

drawwindow(win: ref Window)
{
    display := win.display;
    image := win.image;
    
    # Allocate a new Image to the backbuffer
    backbuffer = display.newimage(image.r, image.chans, 0, Draw->Black);
    backbufrect = backbuffer.r;
    
    # Draw the background
    backbuffer.draw(backbufrect, bgcolor, nil, backbufrect.min);
    
    # We can do all of the other drawing operations here
    # Things such as Fonts, Sprites, Missiles, BoB's
    # Yeah!!
    
    # Draw some test
    drawscoreboard( backbuffer );
    
    # Flush all drawing operations on the backbuffer
    backbuffer.flush(Draw->Flushnow);
    
    # Copy the backbuffer to the Window Image
    image.draw(image.r, backbuffer, nil, image.r.min);
    
    # Flush all drawing operations on the framebuffer
    image.flush(Draw->Flushnow);
}

drawscoreboard(image: ref Image)
{
    # Lets draw some Text in the top left corner
    rectmx  := sys->sprint("Rect Min-X: %d", image.r.min.x);
    rectmy  := sys->sprint("Rect Min-Y: %d", image.r.min.y);
    rectax  := sys->sprint("Rect Max-X: %d", image.r.max.x);
    rectay  := sys->sprint("Rect Max-Y: %d", image.r.max.y);
    mousex := sys->sprint("Mouse X: %d", mouse.x);
    mousey := sys->sprint("Mouse Y: %d", mouse.y);
    mouseb := sys->sprint("Mouse Button: %d", mouse.buttons);
    
    drawtext( image, mousex, textfont, yellow, 20, 10 );
    drawtext( image, mousey, textfont, yellow, 20, 20 );
    drawtext( image, mouseb, textfont, yellow, 20, 30 );
    drawtext( image, rectmx, textfont, yellow, 20, 40 );
    drawtext( image, rectmy, textfont, yellow, 20, 50 );
    drawtext( image, rectax, textfont, yellow, 20, 60 );
    drawtext( image, rectay, textfont, yellow, 20, 70 );
}